<template>
  <div class="flex flex-col h-screen" style="overflow: auto">
    <div
      class="flex flex-nowrap fixed w-full items-baseline top-0 px-6 py-4 bg-gray-100"
      style="background-color: #282829"
    >
      <!-- <div class="flex-no"> 01</div> -->
      <div class="flex-1 text-2xl font-bold" style="color: aliceblue; text-align: center">
        洽谈 CharTen
        <span class="flex-no ml-4 text-sm text-gray-500" style="color: aliceblue">
          企业自有智能 AI 体
        </span>
      </div>
      <!-- <div class="flex-no ml-4 text-sm text-gray-500" style="color: aliceblue;">
        企业自有智能 AI 体
      </div> -->
      <!-- <div
        class="ml-auto px-3 py-2 text-sm cursor-pointer hover:bg-white rounded-md"
        @click="clickConfig()"
      >
        设置
      </div> -->
    </div>

    <div class="flex-1 mx-2 mt-20 mb-2" ref="chatListDom" @click="offvisible">
      <div
        v-for="(item, index) of messageList.filter((v) => v.role !== 'system')"
        :class="
          'group flex flex-col px-4 py-3' +
          (index % 2 == 0 ? ' cocol' : '') +
          ' rounded-lg'
        "
      >
        <!-- {{index % 2 == 0}} -->
        <div class="">
          <div class="flex justify-between items-center mb-2">
            <div class="font-bold">{{ roleAlias[item.role] }}：</div>
            <Copy class="invisible group-hover:visible" :content="item.content" />
          </div>
          <div>
            <div
              class="text-sm text-slate-600 leading-relaxed"
              v-if="item.content"
              v-html="md.render(item.content)"
            ></div>
            <Loding v-else />
          </div>
        </div>

        <a-modal v-model:open="open" title="文件已存在!" @ok="handleOk">
          <template #footer>
            <a-button key="back" @click="handleCancel">Return</a-button>
            <a-button key="submit" type="primary" :loading="loading" @click="handleOk"
              >Submit</a-button
            >
          </template>
          <p>是否更新该文档?</p>
        </a-modal>
      </div>
    </div>

    <div class="sticky bottom-0 w-full p-4 pt-2 pb-8 bg-gray-100">
      <div class="-mt-2 mb-2 text-sm text-gray-500" v-if="isConfig">请输入 API Key：</div>
      <div class="mb-2 ml-2" v-if="!ismobi">
        <a-popover title placement="topLeft">
          <template #content>
            <label for="cowbell" style="position: relative; top: -4px">
              &nbsp;精度: &nbsp;</label
            >
            <input
              type="range"
              id="cowbell"
              name="cowbell"
              min="200"
              max="1000"
              v-model="diatance"
              step="100"
            />
          </template>
          <!-- <a-button type=""><SettingTwoTone /></a-button> -->
          <!-- <SettingTwoTone v-permission="'tool'"/> -->
          <SettingTwoTone />
        </a-popover>

        <a-popover title>
          <template #content>
            <div class="mb-5">
              WORD、PDF文档
              <a-switch
                :checked="isKnowledge"
                class="bg-gray-400"
                checked-children="启用"
                un-checked-children="关闭"
                @click="dlexpertsw('ik')"
              />
            </div>
            <div class="mb-5">
              EXCEL
              <a-switch
                :checked="isKnowledgeex"
                class="bg-gray-400"
                checked-children="启用"
                un-checked-children="关闭"
                @click="dlexpertsw('ikex')"
              />
            </div>
          </template>
          <a-button type=""
            >本地知识库
            <HddTwoTone />
          </a-button>
        </a-popover>

        <a-popover title>
          <template #content>
            <div class="mb-5">
              电力专家
              <a-switch
                :checked="dlexpert"
                class="bg-gray-400"
                checked-children="启用"
                un-checked-children="关闭"
                @click="dlexpertsw('dl')"
              />
            </div>
          </template>
          <a-button class="ml-5" type="">行业专家库 &nbsp;</a-button>
        </a-popover>
        <span v-show="dlexpert">【已启用{{ dlexpert ? "电力" : "" }}专家】</span>
        <span v-show="!dlexpert"
          >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span
        >
        <a-popover title>
          <template #content>
            <div class="mb-5">
              天气查询
              <a-switch
                :checked="weathertool"
                class="bg-gray-400"
                checked-children="启用"
                un-checked-children="关闭"
                @click="dlexpertsw('wt')"
              />
            </div>
            <div class="mb-5">
              三相电流计算
              <!-- 日志查询 -->
              <a-switch
                :checked="electrictool"
                class="bg-gray-400"
                checked-children="启用"
                un-checked-children="关闭"
                @click="dlexpertsw('et')"
              />
            </div>
            <!-- <div class="mb-5">
              制造专家
              <a-switch
                :checked="zzexpert"
                class="bg-gray-400"
                checked-children="启用"
                un-checked-children="关闭"
                @click="dlexpertsw('zz')"
              />
            </div> -->
          </template>
          <a-button class="ml-5" type="">工具 &nbsp;</a-button>
          <!-- <a-button class="ml-5" type="">行业专家库 &nbsp;</a-button> -->
        </a-popover>
        <a-button class="ml-5" type="">
          <CustomerServiceTwoTone />
          AI 售前客服 &nbsp;</a-button
        >
        <a-switch
          :checked="aisale"
          class="bg-gray-400"
          checked-children="启用"
          un-checked-children="关闭"
          @click="dlexpertsw('ai')"
        />
      </div>
      <div class="mb-2 ml-2" v-else>
        <a-popover title placement="topLeft">
          <template #content>
            <label for="cowbell" style="position: relative; top: -4px">
              &nbsp;精度: &nbsp;</label
            >
            <input
              type="range"
              id="cowbell"
              name="cowbell"
              min="200"
              max="1000"
              v-model="diatance"
              step="100"
            />
          </template>
          <SettingTwoTone class=""/>
        </a-popover>

        <a-popover title>
          <template #content>
            <div class="mb-5">
              WORD、PDF文档
              <a-switch
                :checked="isKnowledge"
                class="bg-gray-400"
                checked-children="启用"
                un-checked-children="关闭"
                @click="dlexpertsw('ik')"
              />
            </div>
            <div class="mb-5">
              EXCEL
              <a-switch
                :checked="isKnowledgeex"
                class="bg-gray-400"
                checked-children="启用"
                un-checked-children="关闭"
                @click="dlexpertsw('ikex')"
              />
            </div>
          </template>
          <a-button type="" class="ml-10"
            >本地知识库
            <HddTwoTone />
          </a-button>
        </a-popover>

        <a-popover title>
          <template #content>
            <div class="mb-5">
              电力专家
              <a-switch
                :checked="dlexpert"
                class="bg-gray-400"
                checked-children="启用"
                un-checked-children="关闭"
                @click="dlexpertsw('dl')"
              />
            </div>
          </template>
          <a-button class="ml-5" type="">行业专家库 &nbsp;</a-button>
        </a-popover>

        <div>
          <a-popover title>
            <template #content>
              <div class="mb-5">
                天气查询
                <a-switch
                  :checked="weathertool"
                  class="bg-gray-400"
                  checked-children="启用"
                  un-checked-children="关闭"
                  @click="dlexpertsw('wt')"
                />
              </div>
              <div class="mb-5">
                三相电流计算
                <a-switch
                  :checked="electrictool"
                  class="bg-gray-400"
                  checked-children="启用"
                  un-checked-children="关闭"
                  @click="dlexpertsw('et')"
                />
              </div>
              <!-- <div class="mb-5">
              制造专家
              <a-switch
                :checked="zzexpert"
                class="bg-gray-400"
                checked-children="启用"
                un-checked-children="关闭"
                @click="dlexpertsw('zz')"
              />
            </div> -->
            </template>
            <a-button class="ml-14" type="">工&nbsp;&nbsp;具 &nbsp;</a-button>
            <!-- <a-button class="ml-5" type="">行业专家库 &nbsp;</a-button> -->
          </a-popover>



          <a-popover title>
            <template #content>
              <a-switch
            :checked="aisale"
            class="bg-gray-400"
            checked-children="启用"
            un-checked-children="关闭"
            @click="dlexpertsw('ai')"
          />
            </template>
            <a-button class="ml-14" type="">
            <CustomerServiceTwoTone />
            AI 售前客服 &nbsp;</a-button
          >
            <!-- <a-button class="ml-5" type="">行业专家库 &nbsp;</a-button> -->
          </a-popover>
        </div>
      </div>
      <div class="flex">
        <input
          class="input"
          :type="isConfig ? 'password' : 'text'"
          :placeholder="isConfig ? 'sk-xxxxxxxxxx' : '请输入'"
          v-model="messageContent"
          style="min-width: 10px"
          @keydown.enter="
            isKnowledge || dlexpert || zzexpert
              ? sendOrSavev2('rag')
              : sendOrSavev2('llm')
          "
        />
        <a-tooltip title="语音转文字">
          <a-button
            type=""
            shape="circle"
            :icon="h(AudioOutlined)"
            @mousedown.native="onSpeak"
            @mouseup.native="sendSpeak"
            circle
            style="position: relative; left: -50px; top: 3px"
          />
        </a-tooltip>
        <button
          class="btn"
          :disabled="isTalking"
          @click="
            isKnowledge || isKnowledgeex || dlexpert || zzexpert
              ? sendOrSavev2('rag')
              : sendOrSavev2('llm')
          "
        >
          {{ isConfig ? "保存" : "发送" }}
        </button>

        <a-popover title placement="topLeft" :open="visible">
          <template #content>
            <a-spin :spinning="dboptions.dbloading">
              <a-select
                v-model:value="dboptions.dbvalue"
                style="width: 120px"
                :loading="dboptions.dbloading"
                :options="dboptions.dblist"
                placeholder="请选择知识库"
                @focus="focus"
                @change="handleChange"
                @blur="offvisible"
              ></a-select>
            </a-spin>
          </template>

          <!-- <a-button type="">设置 &nbsp;</a-button> -->
          <button
            class="btn ml-2"
            :disabled="isTalking"
            @click="fileupload()"
            @mouseenter="onvisible"
          >
            文件上传
          </button>
        </a-popover>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import type { ChatMessage } from "@/types";
import { ref, watch, nextTick, onMounted, reactive } from "vue";
import { chat } from "@/libs/gpt";
import { chatllm, chatrag, chatragex, chattool, chataisale } from "@/libs/gptv2";
import cryptoJS from "crypto-js";
import Loding from "@/components/Loding.vue";
import Copy from "@/components/Copy.vue";
import { md } from "@/libs/markdown";
import { h } from "vue";
import {
  SearchOutlined,
  AudioOutlined,
  SettingTwoTone,
  HddTwoTone,
  CustomerServiceTwoTone,
} from "@ant-design/icons-vue";

import Recorder from "js-audio-recorder";
import { message } from "ant-design-vue";

import { useRouter } from "vue-router";
const router = useRouter();

import { userPermissionsStore } from "@/store/index";
// import { storeToRefs } from 'pinia';
// const store = userPermissionsStore()
// const { userPermissions } = storeToRefs<any>(store)

// const vPermission = {
//   mounted(el:any, binding:any) {
//     const requiredPermission = binding.value;
//     console.log(userPermissions.value, requiredPermission);

//     if (!userPermissions.value.includes(requiredPermission)) {
//       el.style.display = 'none';
//     }
//   }
// }

let apiKey = "";
let isConfig = ref(false);
let isTalking = ref(false);
let messageContent = ref("");
const chatListDom = ref<HTMLDivElement>();
const decoder = new TextDecoder("utf-8");
const roleAlias = { user: "ME", assistant: "CharTen", system: "System" };
const messageList = ref<ChatMessage[]>([
  {
    role: "system",
    content: "你是 ChatGPT，OpenAI 训练的大型语言模型，尽可能简洁地回答。",
  },
  //   {
  //     role: "assistant",
  //     content: `你好，我是AI语言模型，我可以提供一些常用服务和信息，例如：

  // 1. 翻译：我可以把中文翻译成英文，英文翻译成中文，还有其他一些语言翻译，比如法语、日语、西班牙语等。

  // 2. 咨询服务：如果你有任何问题需要咨询，例如健康、法律、投资等方面，我可以尽可能为你提供帮助。

  // 3. 闲聊：如果你感到寂寞或无聊，我们可以聊一些有趣的话题，以减轻你的压力。

  // 请告诉我你需要哪方面的帮助，我会根据你的需求给你提供相应的信息和建议。`,
  //   },
  // {
  //   role: "assistant",
  //   content: `人类进化是一个漫长的过程，可以追湖到教百万年前。在进化过程中，生物通过不断地适应环境、遗传变异和自然选择来逐渐发展和演化。经过长时间的自然选择和演化，人类逐渐形成了现在的心态、行为和生理特征。环境的变化是推动人类发展的主要因素，如气候、地形、植被等。在进化过程中，人类祖先逐渐演化出智慧、社会性和文化等特质，这些特质在人类的发展史上起到了关键作用。总的来说，人类进化是一个复杂的过程，涉及众多因素。`,
  // },
  // {
  //   role: "user",
  //   content: `介绍一下微服务`,
  // },
  // {
  //   role: "assistant",
  //   content: `微服务是一种软件架构风格，它基于面向服务的架构(SOA)理念，将应用程序拆分成一系列小型、独立的服务单元，每个服务单元可以独立开发、部署、升级和扩展，共通过轻量级的通信动议和接口进行交石。这些服务单元通学具有阳确定义的边界和职责，可以独立完成特定的功能任务、微股务架构有利干提高系统的可扩展性、灵活性和可维护性，同时降低应用程序的复杂性。`,
  // },
  // {
  //   role: "user",
  //   content: `常州天气`,
  // },
  // {
  //   role: "assistant",
  //   content: `常州天气是阴，气温为10摄氏度。`,
  // },
  // {
  //   role: "user",
  //   content: `减速机异响原因`,
  // },
  // {
  //   role: "assistant",
  //   content: `减速机异响的原因可能包括。减速机铸造杂质未去除，运行中存在非周期性间断摩擦声;长期润滑不良，轴承损坏，输出端安装传动元件损伤减读机。`,
  // },
  // {
  //   role: "user",
  //   content: `三相电源不平衡是为什么`,
  // },
  // {
  //   role: "assistant",
  //   content: `三相电源不平衡的原因主要有电机定子绕组匝间短路、接线错误和三相电源电压不平衡等。为了解决这个问题，可以采取检测、修理绕组，改正接线和改善供电质量等措施。`,
  // },
  // {
  //   role: "user",
  //   content: `我要供应商130311的委外发出单信息`,
  // },
  // {
  //   role: "assistant",
  //   content: `根据您的要求，我已经成功调用了供应商130311的委外发出单信息AP，共将结果返回给您了。请查看附件中的开票信息。如果您有任何其他问题或需要进一步的帮助，请随时告诉我。`,
  // },
  {
    role: "assistant",
    content: `让我们开始愉快地交谈，如果您想提问知识库内容请打开知识库开关。`,
  },
]);

let ismobi = ref(false);

onMounted(() => {
  if (getAPIKey()) {
    switchConfigStatus();
  }

  if (/Mobi|Android|iPhone/i.test(navigator.userAgent)) {
    // 当前设备是移动设备
    ismobi.value = true;
    console.log("ismobi");
  } else {
    ismobi.value = false;
    console.log("is not mobi");
  }
});

let visible = ref<boolean>(false);
const onvisible = () => {
  visible.value = true;
};
const offvisible = () => {
  visible.value = false;
};
let dboptions = reactive<any>({
  dblist: [],
  dbvalue: "",
  dbloading: false,
});

const focus = async () => {
  // console.log("focus");
  dboptions.dblist = [];
  dboptions.dbloading = true;
  const res = await fetch("/api/v1/vector/databases", {
    method: "get",
    headers: {
      Authorization: `Bearer ${localStorage.getItem("token")}`,
    },
  });
  let data = await res.json();
  console.log(data);
  data.results.forEach((item: any) => {
    console.log(item);
    dboptions.dblist.push({
      value: item,
      // label: item,
    });
  });
  dboptions.dbloading = false;
  console.log(dboptions.dblist);
  // data.results
  // dboptions.dblist = [
  //   {
  //     value: "jack",
  //   },
  //   {
  //     value: "lucy",
  //   },
  //   {
  //     value: "disabled",
  //     label: "Disabled",
  //     disabled: true,
  //   }
  // ]
};

const handleChange = (value: string) => {
  console.log(`selected ${value}`);
};

// 语音转文字
let recorder: any;
const initRecorder = () => {
  // 服务器需要https协议或者设置一下浏览器
  // console.log(navigator);

  if (!navigator.mediaDevices) {
    message.info("当前没有语音权限！");
    // console.log("当前没有语音权限！");
    return;
  }
  if (navigator.mediaDevices.getUserMedia) {
    const constraints = { audio: true };
    navigator.mediaDevices.getUserMedia(constraints).then(
      () => {
        console.log("授权成功！");
      },
      () => {
        console.error("授权失败！");
      }
    );
  } else {
    console.error("浏览器不支持 getUserMedia");
  }
  recorder = new Recorder({
    sampleBits: 16, // 采样位数，支持 8 或 16，默认是16
    sampleRate: 16000, // 采样率，支持 11025、16000、22050、24000、44100、48000，根据浏览器默认值，我的chrome是48000
    numChannels: 1, // 声道，支持 1 或 2， 默认是1
  });
};
initRecorder();
let infomessage: any;
const onSpeak = () => {
  recorder.start().then(function () {
    infomessage = message.info("正在讲话...");
  });
  console.log("开始说话");
};
const sendSpeak = async () => {
  // 结束录音
  // infomessage();
  // setTimeout(, 1000)
  recorder.stop();
  recorder.stop();
  // 录音播放
  recorder.play();
  // 获取 WAV 数据(Blob)
  let blob = recorder.getWAVBlob();
  console.log(blob);
  // 将WAV转化，获取 MP3 数据(Blob)，函数wavToMp3、blobToBase64见下两个代码块
  // const mp3Blob = wavToMp3(this.recorder.getWAV(),this.recorder.getChannelData());
  // this.blobToBase64(mp3Blob).then(stream => {
  // 	console.log('语音打印', stream)
  // 下面发送数据
  // })
  let speakformData = new FormData();
  speakformData.append("file", blob);
  const res = await fetch("/speak/v1/asr/stream", {
    method: "post",
    body: speakformData,
    // dataType: "text/event-stream",
    headers: {
      Authorization: `Bearer ${localStorage.getItem("token")}`,
    },
  });
  if (!res.ok) {
    throw new Error(`HTTP error! status: ${res.status}`);
  }
  if (res.body) {
    const reader = res.body.getReader();
    let decoder = new TextDecoder();
    // let resultData = "";
    let result = true;
    while (result) {
      const { done, value } = await reader.read();
      if (done) {
        console.log("Stream ended");
        result = false;
        break;
      }
      messageContent.value += decoder.decode(value).replace("data: ", "");
    }
  }
  console.log("发送语音");
};

let diatance = ref<number>(400);
let isKnowledge = ref(false);
let isKnowledgeex = ref(false);

let dlexpert = ref(false);
let zzexpert = ref(false);

let weathertool = ref(false);
let electrictool = ref(false);

let aisale = ref(false);

const dlexpertsw = (value: string) => {
  if (value == "dl") {
    if (dlexpert.value == true) {
      dlexpert.value = false;
    } else {
      isKnowledge.value = false;
      isKnowledgeex.value = false;
      aisale.value = false;
      zzexpert.value = false;
      dlexpert.value = true;
      weathertool.value = false;
      electrictool.value = false;
    }
  } else if (value == "zz") {
    if (zzexpert.value == true) {
      zzexpert.value = false;
    } else {
      isKnowledge.value = false;
      isKnowledgeex.value = false;
      aisale.value = false;
      dlexpert.value = false;
      zzexpert.value = true;
      weathertool.value = false;
      electrictool.value = false;
    }
  } else if (value == "ik") {
    if (isKnowledge.value == true) {
      isKnowledge.value = false;
    } else {
      dlexpert.value = false;
      zzexpert.value = false;
      isKnowledge.value = true;
      isKnowledgeex.value = false;
      aisale.value = false;
      weathertool.value = false;
      electrictool.value = false;
    }
  } else if (value == "ikex") {
    if (isKnowledgeex.value == true) {
      isKnowledgeex.value = false;
    } else {
      dlexpert.value = false;
      zzexpert.value = false;
      isKnowledge.value = false;
      isKnowledgeex.value = true;
      aisale.value = false;
      weathertool.value = false;
      electrictool.value = false;
    }
  } else if (value == "ai") {
    if (aisale.value == true) {
      aisale.value = false;
    } else {
      dlexpert.value = false;
      zzexpert.value = false;
      isKnowledge.value = false;
      isKnowledgeex.value = false;
      aisale.value = true;
      weathertool.value = false;
      electrictool.value = false;
    }
  } else if (value == "wt") {
    if (weathertool.value == true) {
      weathertool.value = false;
    } else {
      dlexpert.value = false;
      zzexpert.value = false;
      isKnowledge.value = false;
      isKnowledgeex.value = false;
      aisale.value = false;
      weathertool.value = true;
    }
  } else if (value == "et") {
    if (electrictool.value == true) {
      electrictool.value = false;
    } else {
      dlexpert.value = false;
      zzexpert.value = false;
      isKnowledge.value = false;
      isKnowledgeex.value = false;
      aisale.value = false;
      electrictool.value = true;
    }
  }
};

const open = ref<boolean>(false);
const confirmLoading = ref<boolean>(false);

let formData = new FormData();

const fileupload = () => {
  console.log(diatance.value);
  const input = document.createElement("input");
  input.type = "file";
  // input.accept = ".xlsx,.xls";
  input.click();
  input.onchange = async () => {
    const file: any = input.files![0];
    // const formData = new FormData();
    const filepath = URL.createObjectURL(file);
    console.log(filepath);
    formData.delete("file");
    formData.append("file", file);
    if (dboptions.dbvalue) {
      formData.delete("db");
      formData.append("db", dboptions.dbvalue);
    } else {
      formData.delete("db");
      formData.append("db", "default");
    }
    console.log(file.path);

    // if (file.path) {
    //   formData.delete("file_path");
    //   formData.append("file_path ", file.path);
    // } else {
    //   formData.delete("file_path");
    //   formData.append("file_path ", file.name);
    // }
    const res = await fetch("/file/v1/document/upload", {
      method: "post",
      body: formData,
      headers: {
        Authorization: `Bearer ${localStorage.getItem("token")}`,
      },
    });
    // open.value = true
    if (res) {
      console.log(1122);
    } else {
      console.log(333);
    }
    if (res.status == 400) {
      message.error("文件上传失败!");
      open.value = true;
    } else if (res.status == 200) {
      message.success("文件上传成功!");
    } else {
      message.error("通信异常!");
    }
    // console.log(res.status);
    // const data = await res.json();
  };
};
const loading = ref<boolean>(false);

const handleOk = async () => {
  loading.value = true;
  const res = await fetch("/file/v1/document/update", {
    method: "post",
    body: formData,
    headers: {
      Authorization: `Bearer ${localStorage.getItem("token")}`,
    },
  });
  if (res.status == 200) {
    message.success("文件更新成功!");
    open.value = false;
  } else {
    message.error("文件更新失败!");
    open.value = false;
  }
  loading.value = false;
};

const handleCancel = () => {
  open.value = false;
};

let abortController: AbortController = new AbortController();
let shouldCancelReadChunk: boolean[] = [];

const output = ref<string>("");
const getGPTAnswer = async (messages: any) => {
  abortController.abort(); // 取消当前请求
  abortController = new AbortController(); // 创建新的 AbortController
  // 将 shouldCancelReadChunk 的值设置为 true
  shouldCancelReadChunk.forEach((_, idx) => {
    shouldCancelReadChunk[idx] = true;
  });
  shouldCancelReadChunk.push(false);
  let index = shouldCancelReadChunk.length - 1;

  try {
    let result = "";
    // output.value = '请求中...'
    const res = await fetch("/api/Hello", {
      method: "get",
      headers: {
        "Content-Type": "application/json",
      },
      signal: abortController.signal, // 绑定 AbortSignal 到请求
    });
    if (res.body == null) {
      return;
    }

    const reader = res.body.getReader();
    const decoder = new TextDecoder("utf-8");
    const readChunk = async (): Promise<void> => {
      // 在循环内检查 shouldCancelReadChunk 的值
      if (shouldCancelReadChunk[index]) {
        // 如果需要取消，则提前返回并停止执行 readChunk
        return;
      }
      const { value, done } = await reader.read();

      if (!done) {
        const dataString = decoder.decode(value);
        console.log(dataString);

        const lines = dataString.trim().split("data:");
        for (const line of lines) {
          // 在循环内检查 shouldCancelReadChunk 的值
          if (shouldCancelReadChunk[index]) {
            // 如果需要取消，则提前返回并停止执行
            return;
          }
          if (line != "") {
            console.log(line);
            const text = line.replace("data:", "").trim();
            console.log(text);
            try {
              const data = JSON.parse(text);
              if (data.data) {
                result += data.data;
                // console.log(result);
              }
              // if (data.data === "length") {
              //   messages.push({
              //     role: "assistant",
              //     content: result,
              //   });
              //   appendLastMessageContent(result);
              //   await getGPTAnswer(messages);
              //   return;
              // } else if (data.data === "stop") {
              //   // output.value = result;
              //   appendLastMessageContent(result);
              //   return;
              // }
            } catch (error: any) {
              if (error && error.code && error.code === 20) {
                console.log("请求被用户终止");
                return;
              }
              if (text.trim() === "[DONE]") {
                // output.value = result;
                appendLastMessageContent(result);
                return;
              }
              console.error(error);
              return;
            }
          }
        }
        appendLastMessageContent(result);
        await readChunk(); // 如果没有触发结束标识，则继续读取
      } else {
        console.log("done");
      }
    };
    await readChunk();
  } catch (error: any) {
    if (error.name === "AbortError") {
      console.log("Request was canceled");
    } else {
      console.error(error);
    }
  }
};

const sendChatMessage = async (content: string = messageContent.value) => {
  try {
    isTalking.value = true;
    if (messageList.value.length === 2) {
      messageList.value.pop();
    }
    messageList.value.push({ role: "user", content });
    clearMessageContent();
    messageList.value.push({ role: "assistant", content: "" });

    const { body, status } = await chat(messageList.value, getAPIKey());
    if (body) {
      const reader = body.getReader();
      await readStream(reader, status);
    }
  } catch (error: any) {
    appendLastMessageContent(error);
  } finally {
    isTalking.value = false;
  }
};
const sendChatMessagev2 = async (
  type: string = "llm",
  content: string = messageContent.value
) => {
  try {
    isTalking.value = true;
    if (messageList.value.length === 2) {
      messageList.value.pop();
    }
    messageList.value.push({ role: "user", content });
    clearMessageContent();
    messageList.value.push({ role: "assistant", content: "" });
    let database = "";
    if (dlexpert.value) {
      database = "power_industry";
    }
    if (isKnowledge.value) {
      database = "default";
    }
    let tool: any = {};
    if (weathertool.value) {
      tool.weather = "city";
    }
    if (electrictool.value) {
      // tool.calculate_electric_current = "power, voltage";
      tool.get_log = "start_time, end_time, trace_id";
    }
    // const { response, reference, history } =
    const res =
      type == "llm"
        ? (weathertool.value || electrictool.value
          ? await chattool({ role: "user", content }, getAPIKey(), tool)
          : aisale.value
          ? await chataisale(
              { role: "user", content },
              getAPIKey(),
              // Number(diatance.value)
              Number(600)
            )
          : await chatllm({ role: "user", content }, getAPIKey()))
        : isKnowledge.value || isKnowledgeex.value || dlexpert.value
        ? await chatrag(
            { role: "user", content },
            getAPIKey(),
            Number(diatance.value),
            database
          )
        : await chatragex(
            { role: "user", content },
            getAPIKey(),
            Number(diatance.value),
            database
          );
    if (res.status == 401) {
      message.error("登录超时请重新登录");
      router.push("/login");
    } else {
      const { response, reference, history } = await res.json();
      if (response) {
        // const reader = body.getReader();
        // await readStream(reader, status);
        appendLastMessageContent(response);
      }
      if (reference) {
        // const reader = body.getReader();
        // await readStream(reader, status)
        appendLastMessageContent("\n———————————————————————————————————————————————");
        reference.forEach((item: string, index: number) => {
          if (index % 2 == 0) {
            appendLastMessageContent("\n");
            appendLastMessageContent("【" + item + "】");
          } else {
            appendLastMessageContent("【" + item + "】");
          }
        });
      }
    }
  } catch (error: any) {
    appendLastMessageContent(error);
  } finally {
    isTalking.value = false;
  }

  //   let reference = [
  //   "文档源:D:\\sw\\苏文项目\\2023\\33理想东区充电场站\\结算资料\\竣工资料\\常山服务区昆明方向.pdf ",
  //   "页码:30",
  //   "文档源:D:\\sw\\苏文项目\\2023\\33理想东区充电场站\\结算资料\\竣工资料\\常山服务区昆明方向.pdf ",
  //   "页码:18",
  //   "文档源:D:\\sw\\苏文项目\\2023\\33理想东区充电场站\\结算资料\\竣工资料\\常山服务区昆明方向.pdf ",
  //   "页码:35"
  // ]
};

const readStream = async (
  reader: ReadableStreamDefaultReader<Uint8Array>,
  status: number
) => {
  let partialLine = "";

  while (true) {
    // eslint-disable-next-line no-await-in-loop
    const { value, done } = await reader.read();
    if (done) break;

    const decodedText = decoder.decode(value, { stream: true });

    if (status !== 200) {
      const json = JSON.parse(decodedText); // start with "data: "
      const content = json.error.message ?? decodedText;
      appendLastMessageContent(content);
      return;
    }

    const chunk = partialLine + decodedText;
    const newLines = chunk.split(/\r?\n/);

    partialLine = newLines.pop() ?? "";

    for (const line of newLines) {
      if (line.length === 0) continue; // ignore empty message
      if (line.startsWith(":")) continue; // ignore sse comment message
      if (line === "data: [DONE]") return; //

      const json = JSON.parse(line.substring(6)); // start with "data: "
      const content =
        status === 200 ? json.choices[0].delta.content ?? "" : json.error.message;
      appendLastMessageContent(content);
    }
  }
};
// 向最后一个添加文字
const appendLastMessageContent = (content: string) =>
  (messageList.value[messageList.value.length - 1].content += content);

const sendOrSave = () => {
  if (!messageContent.value.length) return;
  console.log(111);
  console.log(isConfig.value);
  if (isConfig.value) {
    console.log(22);
    if (saveAPIKey(messageContent.value.trim())) {
      console.log(33);
      switchConfigStatus();
    }
    clearMessageContent();
  } else {
    console.log(44);
    sendChatMessage();
  }
};

const sendOrSavev2 = (type: string) => {
  if (!messageContent.value.length) return;
  if (isConfig.value) {
    if (saveAPIKey(messageContent.value.trim())) {
      switchConfigStatus();
    }
    clearMessageContent();
  } else {
    sendChatMessagev2(type);
  }
};

const clickConfig = () => {
  if (!isConfig.value) {
    messageContent.value = getAPIKey();
  } else {
    clearMessageContent();
  }
  switchConfigStatus();
};

const getSecretKey = () => "lianginx";

const saveAPIKey = (apiKey: string) => {
  if (apiKey.slice(0, 3) !== "sk-" || apiKey.length !== 51) {
    alert("API Key 错误，请检查后重新输入！");
    return false;
  }
  const aesAPIKey = cryptoJS.AES.encrypt(apiKey, getSecretKey()).toString();
  localStorage.setItem("apiKey", aesAPIKey);
  return true;
};

const getAPIKey = () => {
  if (apiKey) return apiKey;
  const aesAPIKey = localStorage.getItem("apiKey") ?? "";
  apiKey = cryptoJS.AES.decrypt(aesAPIKey, getSecretKey()).toString(cryptoJS.enc.Utf8);
  return apiKey;
};

const switchConfigStatus = () => (isConfig.value = !isConfig.value);

const clearMessageContent = () => (messageContent.value = "");

const scrollToBottom = () => {
  if (!chatListDom.value) return;
  scrollTo(0, chatListDom.value.scrollHeight);
};

watch(messageList.value, () => nextTick(() => scrollToBottom()));
</script>

<style scoped>
pre {
  font-family: -apple-system, "Noto Sans", "Helvetica Neue", Helvetica, "Nimbus Sans L",
    Arial, "Liberation Sans", "PingFang SC", "Hiragino Sans GB", "Noto Sans CJK SC",
    "Source Han Sans SC", "Source Han Sans CN", "Microsoft YaHei", "Wenquanyi Micro Hei",
    "WenQuanYi Zen Hei", "ST Heiti", SimHei, "WenQuanYi Zen Hei Sharp", sans-serif;
}

.cocol {
  background-color: #f5f5f7;
}

/* .cocol:hover {
} */

.group:hover {
  background-color: #f2e9e4;
}
</style>
